#!/bin/bash
#############################################
# author:fang
# version : v1.0
# name  : xgit
# dispcripe: the set of git
# CopyRight@fangyunjiang[42550564@qq.com]
#############################################
source ~/command/common

declare -a _files
declare -a _versions
_backup_path="~/command/gitbackup/"

_dest_dir=""
_filter=""
_logfile=""
_show_status=""
_tool=0
_oper=0

function git_single_command()
{
	local command title disp is_update

	command=$1
	is_update=$2
	title=$3
	disp=$4

	unset _list_
	_list_=("${_files[@]}")
	__radiobox "$command" "$is_update" "$title" "$disp"
}

function git_mult_command()
{
	local command single_command is_update title disp
	command=$1
	single_command=$2
	is_update=$3
	title=$4
	disp=$5

	unset _list_
	_list_=("${_files[@]}")
	__checkbox "$command" "$single_command" "$is_update" "$title" "$disp"
}


function git_show_diff()
{
	local tool
	[ "$_tool" = "0" ] && tool="diff" || tool="difftool"
	git_single_command "git $tool $_TARGET_" "no" "show diff" "select bellow _files to show the difference with git"
}

function git_add_files()
{
	git_mult_command "__exec_command git add $_TARGET_" "" "no" "add files" "select bellow _files to add with git"
}

function git_backup_file()
{
	local file
	file=$_backup_path$(date '+%Y-%m-%d-%H-%M-%S')-$(echo $1|sed 's#/#-#g')
	#echo $file
	cp $1 $file
}

function git_revert()
{
	git_mult_command "git checkout $_TARGET_" "" "yes" "recover" "select bellow _files to revert to git"
	unset _files
	_files=("${_list_[@]}")
}

function git_edit_vi()
{
	git_single_command "vi $_TARGET_" "no" "edit" "select bellow _files to edit with vim"
}

function git_show_version_diff()
{
	ver=$1
	# git_single_command "git diff $ver^ $ver $_TARGET_" "no" "diff" "select bellow _files to show diff with preversion"
	git_single_command "git show $ver" "no" "diff" "select bellow _files to show diff with preversion"
}

function git_log_by_version()
{
	local ver
	ver=$1
	ver=${ver%% *}
	if [ -z "$ver" ];then
		return
	fi
	if [ -n "$_show_status" ];then
		git show $ver
	elif [ -z "$_logfile" ];then
		__show_progressbar 0.1 git_get_log_version_files "$ver"
		git_show_version_diff $ver
		unset _list_
		_list_=("${_versions[@]}")
	else
		git diff $ver^ $ver $_logfile
	fi
}

function git_show_logs()
{
	unset _list_
	_list_=("${_versions[@]}")
	__radiobox "git_log_by_version $_TARGET_" "$no" "logs" "select bellow versions to show details"
}

function git_get_modified_files()
{
	local rs s i var
	rs=$(git status|grep "modified:"|sed 's/.*modified://')
	s=`echo $rs|awk '{split($0,tmp)} END {for(i in tmp) print tmp[i]}'`
	i=0
	unset _files
	for var in $s;do
		_files[i]=$var
		let i++
	done
}

function git_get_log_version_files()
{
	local rs version
	version=$1
	rs=$(git ss --name-status ${version}|grep "^[MA]\s"|sed 's/^[MA]\s*//')
	_files=`echo $rs|awk '{split($0,tmp)} END {for(i in tmp) print tmp[i]}'`
}

function git_get_log_versions()
{
	unset _versions
	eval `git log --oneline ${_filter} ${_logfile}|sed 's/"/\\\\"/g'|awk -v FS=";;;;" '{print "_versions["NR-1"]=\""$1"\""}'`
}

function git_get_add_files()
{
	local rs s i var
	rs=$(git status|grep "modified:"|sed 's/.*modified://')
	s=`echo $rs|awk '{split($0,tmp)} END {for(i in tmp) print tmp[i]}'`
	unset _files
	_files=($(git st|sed '0,/Untracked files:/d;$d;'|sed '1,2d;s/.//'))
	i=${#files[@]}
	for var in $s;do
		_files[i]=$var
		let i++
	done
}
# 如果有di则为比较
function git_copy_change_to()
{
	local var path name di dest_dir
	di=${_dest_dir%:*}
	dest_dir=${_dest_dir#*:}
	if [ "$di" = "$_dest_dir" ];then
		di=""
	fi
	[ -z "$dest_dir" ]&&return
	if [ -n "$di" ];then
		for var in "${_files[@]}";do
			__diff "$var" "$dest_dir/$var"
		done
		return
	fi

	for var in "${_files[@]}";do
		path=$(dirname "$var")
		path=${path##*..}
		[ "$path" = "." ]&&path=""
		name=$(basename "${var}")
		mkdir -p "${dest_dir}/${path}"
		cp -f "${var}" "${dest_dir}/${path}"
		echo "have do copy ${var} to ${dest_dir}"
	done
}

function git_copy_history_change_to()
{
	local front filter path name di dest_dir
	if ! [ -a ".git" ];then
		echo "请在项目根目录运行"
		return
	fi

	front=${_dest_dir%:*}
	dest_dir=${_dest_dir#*:}
	if [ "$front" = "$_dest_dir" ];then
		front=""
	fi
	filter=${front%@*}
	di=${front#*@}
	if [ "$filter" = "$front" ];then
		di=""
	fi
	_files=($(git ss --name-status ${filter}|grep "[MA]\s"|sed 's/[MA]\s*//'))
	if [ -n "$di" ];then
		for var in "${_files[@]}";do
			__diff "$var" "$dest_dir/$var"
		done
		return
	fi
	
	for var in "${_files[@]}";do
		path=$(dirname "$var")
		path=${path##*..}
		[ "$path" = "." ]&&path=""
		name=$(basename "${var}")
		mkdir -p "${dest_dir}/${path}"
		cp -f "${var}" "${dest_dir}/${path}"
		echo "have do copy ${var} to ${dest_dir}"
	done
}

function clear_history_log()
{
	local rs
	if ! [ -a ".git" ];then
		echo "请在项目根目录运行"
		return
	fi
	rs=$(git lg -1|awk -v FS=":| " '{print $2}')
	echo "复制以下的代码进行执行："
	echo ""
	echo ""
	echo "git checkout --orphan $rs"
	echo "git add -A"
	echo "tgit commit -am \"init commit\""
	echo "git branch -D master"
	echo "git branch -m master"
	echo "git push -f origin master"
	echo ""
	echo ""
}

function git_diff_change_with()
{
	[ -z "$_dest_dir" ]&&return
	git_single_command "__diff $_TARGET_ $_dest_dir/$_TARGET_" "no" "show diff" "select bellow _files to show the difference with $_dest_dir"
}

function git_delete_history_force()
{
	local files file OLD_IFS
	OLD_IFS="$IFS"
	IFS=","
	files=($1)
	IFS="$OLD_IFS"
	for file in ${files[@]};do
		git filter-branch --force --index-filter "git rm --cached --ignore-unmatch $file" --prune-empty --tag-name-filter cat -- --all
	done
	rm -rf .git/refs/original/
	git reflog expire --expire=now --all
	git gc --prune=now
	git gc --aggressive --prune=now
}

function find_root_dir() {
	dir=$(pwd)
	while((1));do
		found=$(find ${dir} -name .git)
		if ! [ "${found}" = "" ];then
			break
		fi
		dir=${dir%/*}
	done
	echo ${dir}
}

function git_revert_to_version() {
	local file version
	version=${1%:*}
	file=${1#*:}

	git co $version $file
	cp $file .__file_back
	git co head $file
	diffmerge.sh .__file_back $file
	rm -f .__file_back
}

function git_command() {
	local amend hasupdate hasremote cmd
	amend=`echo "$@"| grep "\--amend"`
	if [ "$amend" != "" -o "$1" = "cihd" -o "$1" = "cihead" -o "$1" = "uphead" ];then
		info=`git st|grep "is ahead of"`
		if [ "$info" = "" ];then
			echo "上次提交已经同步到远端，不能追加提交"
			exit
		fi
	fi
	if [ "$amend" = "" -a "$1" = "commit" -o "$1" = "ci" -o "$1" = "cm" -o "$1" = "cip" -o "$1" = "cmp" ];then
		hasremote=$(git remote -v)
		if [ -n "${hasremote}" ];then
			hasupdate=$(git pl 2>&1|sed -n '/Already up[ -]to[ -]date\|from the remote, but no such ref was fetched./p')
		fi
		if [ -z "$hasupdate" -a -n "${hasremote}" ];then
			echo "解决冲突后再提交，可以使用 git pl 查看具体冲突"
			exit
		fi
	fi
	if [ "$1" = "cip" -o "$1" = "cmp" ];then
		cmd="$1"
		shift
		git ${cmd:0:2} "$@"
		if [ -n "${hasremote}" ];then
			git pu
		fi
	else
		git "$@"
	fi
}

function show_help()
{
	local -a list
	list="help"
	list=("${list[@]}" "Usage :git [OPTIONS]")
	list=("${list[@]}" "  -p [[di:]dest_dir]: 将改变的文件拷贝到 dest_dir 并自动创建文件夹，如果有di则和dest_dir的文件比较")
	list=("${list[@]}" "  -c [-n[@di]:dest_dir]: 将git ss -n的文件拷贝到 dest_dir 并自动创建文件夹，如果有di则和dest_dir的文件比较")
	list=("${list[@]}" "  -i [dest_dir]: 使用di比较改变的文件和dest_dir中备份的文件")
	list=("${list[@]}" "  -l [-n]: 查看log版本列表，进而查看某个版本的文件列表，可以跟过滤条件，如-5")
	list=("${list[@]}" "  -f [file]: 针对-l设置查看文件的log版本列表，如: -f xx.js,只查看xx.js")
	list=("${list[@]}" "  -s: 针对-l，对一个版本执行git show")
	list=("${list[@]}" "  -n [version]: 显示当前版本的log记录")
	list=("${list[@]}" "  -d [tool]: 列出改变文件的列表后选择查看git diff, 0: console 1: tool")
	list=("${list[@]}" "  -a: 列出改变文件的列表后git add")
	list=("${list[@]}" "  -r: 列出改变文件的列表后git revert")
	list=("${list[@]}" "  -e [version:file]: 将某个文件[file]还原到某个版本[version]")
	list=("${list[@]}" "  -v: 列出改变文件的列表后使用vim打开文件")
	list=("${list[@]}" "  -m [file;file]: 强制删除文件的历史记录")
	list=("${list[@]}" "  -u [url]: 重置remote url")
	list=("${list[@]}" "  -g [url]: 从github下载文件或者文件夹")
	list=("${list[@]}" "  -o [url]: 使用gitclone.com下载github工程")
	list=("${list[@]}" "  -w: 清除历史版本")
	list=("${list[@]}" "  -h: show help")
	if [ "$1" = "full" ];then
		list=("${list[@]}" "bref command:")
		list=("${list[@]}" "  co: checkout")
		list=("${list[@]}" "  ci: commit -a")
		list=("${list[@]}" "  cm: commit -m")
		list=("${list[@]}" "  st: status")
		list=("${list[@]}" "  ss: show --stat")
		list=("${list[@]}" "  br: branch")
		list=("${list[@]}" "  di: diff")
		list=("${list[@]}" "  pu: push -u origin master")
		list=("${list[@]}" "  pub: push -u origin")
		list=("${list[@]}" "  puf: push origin master --force")
		list=("${list[@]}" "  pl: pull")
		list=("${list[@]}" "  line: log --pretty=oneline --since='two hours ago'")
		list=("${list[@]}" "  one: log -p -1")
		list=("${list[@]}" "  lg: log --date=short --graph")
		list=("${list[@]}" "  cihd: commit -C HEAD --amend")
		list=("${list[@]}" "  cihead: commit -C HEAD -a --amend")
		list=("${list[@]}" "  uphead: commit --amend -m")
		list=("${list[@]}" "  rshead: reset --hard HEAD")
		list=("${list[@]}" "  rs: reset --hard")
		list=("${list[@]}" "  rsmaster: reset --hard origin/master")
		list=("${list[@]}" "  noadd: rm -r --cached")
		list=("${list[@]}" "  noaddall: rm -r --cached .")
		list=("${list[@]}" "  bare: init --bare")
		list=("${list[@]}" "  remoteadd: remote add origin")
		list=("${list[@]}" "  pupage: push --set-upstream origin gh-pages")
		list=("${list[@]}" "  plpage: pull origin gh-pages gh-pages")
		list=("${list[@]}" "  copage: checkout -b gh-pages origin/gh-pages")
	fi
	__msgbox "${list[@]}"
}

function main()
{
	local oper_right ver files info
	oper_right=0

	if [ "$1" = "--help" ];then
		show_help full
		exit
	fi

	while getopts :p:d:c:si:n:f:m:e:u:g:o:harvwl opt;do
		oper_right=1
		case $opt in
			p)_oper=0;_dest_dir="$OPTARG";;
			c)_oper=10;_dest_dir="$OPTARG";;
			d)_oper=1;_tool="$OPTARG";;
			s)_show_status=1;;
			r)_oper=2;;
			v)_oper=3;;
			a)_oper=4;;
			l)_oper=5;_filter="$OPTARG";;
			f)_logfile="$OPTARG";;
			n)_oper=6;ver="$OPTARG";;
			m)_oper=7;files="$OPTARG";;
			i)_oper=8;_dest_dir="$OPTARG";;
			e)_oper=9;ver="$OPTARG";;
			w)_oper=11;;
			u)git remote remove origin;git remote add origin "$OPTARG";exit;;
			g)xgithub -u "$OPTARG";exit;;
			o)git clone $(echo $OPTARG|sed 's#https://#https://gitclone.com/#');exit;;
			*)show_help;exit;;
		esac
	done

	if [ $oper_right -eq 0 ];then
		git_command "$@"
		exit
	fi

	if ((_oper==7));then
		git_delete_history_force $files
		exit
	elif ((_oper==9));then
		git_revert_to_version $ver
		exit
	elif ((_oper==10));then
		git_copy_history_change_to
		exit
	elif ((_oper==11));then
		clear_history_log
		exit
	fi

	if ((_oper==5));then
		if [ -z "$_show_status" -a -z "$_logfile" ];then
			dir=$(find_root_dir)
			cd ${dir}
		fi
		__show_progressbar 0.1 git_get_log_versions
	elif ((_oper==4));then
		__show_progressbar 0.1 git_get_add_files
	else
		__show_progressbar 0.1 git_get_modified_files
	fi

	case $_oper in
		0)git_copy_change_to;;
		8)git_diff_change_with;;
		1)git_show_diff;;
		2)git_revert;;
		3)git_edit_vi;;
		4)git_add_files;;
		5)git_show_logs;;
		6)git_log_by_version $ver;;
	esac
}

main "$@"
